import { exec } from 'child_process';
import { promisify } from 'util';
import { file } from 'tmp-promise';
import * as fs from 'fs/promises';
import { v4 as uuidv4 } from 'uuid';
import { ApiError } from '../middleware/errorHandler';
import { logger } from '../utils/logger';

const execAsync = promisify(exec);

// 执行结果接口
interface ExecutionResult {
  output: string;
  executionTime: number;
  memoryUsage: number;
  exitCode: number;
}

// 各语言执行配置
const languageConfigs: { [key: string]: { ext: string; runCmd: (filename: string) => string } } = {
  javascript: {
    ext: 'js',
    runCmd: (filename) => `node ${filename}`,
  },
  typescript: {
    ext: 'ts',
    runCmd: (filename) => `ts-node ${filename}`,
  },
  python: {
    ext: 'py',
    runCmd: (filename) => `python3 ${filename}`,
  },
  java: {
    ext: 'java',
    runCmd: (filename) => {
      const className = filename.split('/').pop()?.split('.')[0] || 'Main';
      return `javac ${filename} && java -cp "$(dirname ${filename})" ${className}`;
    },
  },
  go: {
    ext: 'go',
    runCmd: (filename) => `go run ${filename}`,
  },
  rust: {
    ext: 'rs',
    runCmd: (filename) => `rustc ${filename} -o ${filename}.out && ${filename}.out`,
  },
  ruby: {
    ext: 'rb',
    runCmd: (filename) => `ruby ${filename}`,
  },
  cpp: {
    ext: 'cpp',
    runCmd: (filename) => `g++ ${filename} -o ${filename}.out && ${filename}.out`,
  },
  csharp: {
    ext: 'cs',
    runCmd: (filename) => `dotnet script ${filename}`,
  },
  php: {
    ext: 'php',
    runCmd: (filename) => `php ${filename}`,
  },
};

/**
 * 执行代码
 * @param language 编程语言
 * @param code 代码内容
 * @param input 标准输入
 * @param timeout 超时时间（毫秒）
 * @returns 执行结果
 */
export async function executeCode(
  language: string,
  code: string,
  input: string = '',
  timeout: number = 10000
): Promise<ExecutionResult> {
  // 检查语言是否支持
  if (!languageConfigs[language]) {
    throw new ApiError(400, `Unsupported language: ${language}`);
  }

  const startTime = Date.now();
  const executionId = uuidv4();
  logger.info(`Starting execution ${executionId} for ${language} code`);

  try {
    // 创建临时文件
    const { path, cleanup } = await file({
      prefix: `code-${executionId}-`,
      postfix: `.${languageConfigs[language].ext}`,
    });

    // 写入代码到临时文件
    await fs.writeFile(path, code);

    // 构建执行命令
    const runCmd = languageConfigs[language].runCmd(path);
    
    // 执行代码
    const { stdout, stderr } = await Promise.race([
      execAsync(runCmd, {
        timeout,
        input,
        maxBuffer: 1024 * 1024, // 1MB
      }),
      new Promise<never>((_, reject) => {
        setTimeout(() => {
          reject(new ApiError(408, 'Execution timed out'));
        }, timeout);
      }),
    ]);

    // 计算执行时间
    const executionTime = Date.now() - startTime;
    
    // 清理临时文件
    await cleanup();

    // 返回结果
    const output = stdout || stderr;
    logger.info(`Execution ${executionId} completed in ${executionTime}ms`);

    return {
      output: output.trim(),
      executionTime,
      memoryUsage: process.memoryUsage().heapUsed, // 近似值
      exitCode: stderr ? 1 : 0,
    };
  } catch (error) {
    if (error instanceof ApiError) {
      throw error;
    }
    
    const executionTime = Date.now() - startTime;
    const errorMessage = error instanceof Error ? error.message : 'Unknown error';
    
    logger.error(`Execution ${executionId} failed after ${executionTime}ms: ${errorMessage}`);
    
    return {
      output: `Error: ${errorMessage}`,
      executionTime,
      memoryUsage: process.memoryUsage().heapUsed,
      exitCode: 1,
    };
  }
} 